package task

import (
	"sync"
	"time"
)

type Manager struct {
	//任务注册
	Tasks map[string]*Task
	//执行序列
	NextTime  time.Time
	NextTasks []*Task
}

var manager *Manager

func GetManager() *Manager {
	return manager
}

func AddTask(task *Task) {
	manager.Tasks[task.Name] = task
}

// 启动计划监听，非阻塞启动
func Start() {
	manager.Start()
}
func (m *Manager) Start() {
	go m.Starts()
}

// 内部调用阻塞启动
func (m *Manager) Starts() {
	for {
		m.Resort()
		runTime := <-time.After(m.NextTime.Sub(time.Now()))
		for i, _ := range m.NextTasks {
			task := m.NextTasks[i]
			task.LastRun = runTime
			go task.Handler()
		}
	}
}

// 重新排序计算出下一个需要执行的计划任务
func (m *Manager) Resort() {
	m.NextTasks = make([]*Task, 0)
	m.NextTime = time.Now().AddDate(100, 0, 0)
	start := 0
	now := time.Now()
	for name, _ := range m.Tasks {
		task := m.Tasks[name]
		nextTime := task.Timer.GetNext(now, task.LastRun)
		if nextTime.Unix() == task.LastRun.Unix() {
			nextTime = task.Timer.GetNext(now.Add(1*time.Second), task.LastRun)
		}
		if start == 0 {
			m.NextTasks = make([]*Task, 0)
			m.NextTime = nextTime
			m.NextTasks = append(m.NextTasks, task)
		} else {
			//发现了更靠前的
			if nextTime.Unix() < m.NextTime.Unix() {
				m.NextTasks = make([]*Task, 0)
				m.NextTime = nextTime
				m.NextTasks = append(m.NextTasks, task)
			} else if nextTime.Unix() == m.NextTime.Unix() {
				m.NextTasks = append(m.NextTasks, task)
			}
		}
		start++
	}
}

type Task struct {
	Name    string
	Handler Handler
	Timer   *Timer
	LastRun time.Time
}

type Handler func()

// 创建一个新的任务，拥有默认的执行间隔，60秒
func NewTask(name string, handler Handler) *Task {
	t := &Task{
		Name:    name,
		Handler: handler,
		Timer:   NewTimer().SetEvery(60),
		LastRun: time.Now(),
	}
	AddTask(t)
	return t
}

func (t *Task) GetTimer() *Timer {
	return t.Timer
}

func (t *Task) SetTimer(timer *Timer) {
	t.Timer = timer
}

func (t *Task) SetEvery(every int) *Task {
	t.Timer.Every = every
	return t
}

func (t *Task) SetAt(at *TimerAt) *Task {
	t.Timer.At = at
	return t
}
func (t *Task) Add() *Task {
	AddTask(t)
	return t
}

type Timer struct {
	Every int
	At    *TimerAt
}

func NewTimer() *Timer {
	return &Timer{1, nil}
}

// 设置运行间隔
func (t *Timer) SetEvery(second int) *Timer {
	t.At = NewAt()
	t.Every = second
	return t
}
func (t *Timer) SetAt(at *TimerAt) *Timer {
	t.At = at
	return t
}

func (t *Timer) GetNext(now time.Time, lastRun time.Time) time.Time {
	if t.Every == 0 {
		t.Every = 1
	}

	t.At.SecondChan = make(chan int, 10)
	t.At.MinuteChan = make(chan int, 10)
	t.At.HourChan = make(chan int, 10)
	t.At.DayChan = make(chan int, 10)
	t.At.MonthChan = make(chan int, 10)
	t.At.YearChan = make(chan int, 10)

	//记得关闭协程释放资源，否则会出现内存大量占用
	defer func() { t.At.SecondChan <- 9 }()
	defer func() { t.At.MinuteChan <- 9 }()
	defer func() { t.At.HourChan <- 9 }()

	//计算下一个秒的值
	t.At.Wg.Add(1)
	go t.At.GetNextSecond(now, t.Every, lastRun)

	//计算下一个分的值
	t.At.Wg.Add(1)
	go t.At.GetNextMinute(now)

	//计算下一个时的值
	t.At.Wg.Add(1)
	go t.At.GetNextHour(now)

	//周设置与年月日设置是冲突的关系，周设置的优先级高于年月日设置
	//如果设置了周
	if t.At.Week[0] != -1 {
		//计算下一个周的值
		t.At.Wg.Add(1)
		t.At.GetNextWeek(now)
	} else {
		defer func() { t.At.DayChan <- 9 }()
		defer func() { t.At.MonthChan <- 9 }()
		defer func() { t.At.YearChan <- 9 }()

		//计算下一个日的值
		t.At.Wg.Add(1)
		go t.At.GetNextDay(now)

		//计算下一个月的值
		t.At.Wg.Add(1)
		go t.At.GetNextMonth(now)

		//计算下一个年的值
		t.At.Wg.Add(1)
		go t.At.GetNextYear(now)
	}

	t.At.Wg.Wait()

	return time.Date(t.At.YearV, time.Month(t.At.MonthV), t.At.DayV, t.At.HourV, t.At.MinuteV, t.At.SecondV, 0, time.Local)
}

type TimerAt struct {
	Second     []int //0-59
	SecondChan chan int
	SecondV    int
	Minute     []int //0-59
	MinuteChan chan int
	MinuteV    int
	Hour       []int //0-23
	HourChan   chan int
	HourV      int
	Day        []int //1-28-30-31
	DayChan    chan int
	DayV       int
	Week       []int //1-7
	WeekChan   chan int
	WeekV      int
	Month      []int //1-12
	MonthChan  chan int
	MonthV     int
	Year       []int //2022
	YearChan   chan int
	YearV      int
	Done       chan int
	Wg         *sync.WaitGroup
}

func NewAt() *TimerAt {
	return &TimerAt{
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		[]int{-1, -1},
		make(chan int, 10),
		0,
		make(chan int, 10),
		&sync.WaitGroup{},
	}
}
func (at *TimerAt) SetSecond(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Second = val
	return at
}
func (at *TimerAt) SetMinute(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Minute = val
	return at
}
func (at *TimerAt) SetHour(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Hour = val
	return at
}
func (at *TimerAt) SetDay(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Day = val
	return at
}
func (at *TimerAt) SetWeek(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Week = val
	return at
}
func (at *TimerAt) SetMonth(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Month = val
	return at
}
func (at *TimerAt) SetYear(v ...int) *TimerAt {
	val := []int{-1, -1}
	if len(v) == 1 {
		val = []int{v[0], v[0]}
	}
	if len(v) == 2 {
		val = []int{v[0], v[1]}
	}
	at.Year = val
	return at
}

func (at *TimerAt) GetNextSecond(now time.Time, every int, lastRun time.Time) {
	cur := now.Second()
	start := 0
	if at.Second[0] != -1 {
		switch {
		case start < at.Second[0]:
			start = at.Second[0]
			break
		}
		switch {
		case cur <= at.Second[0]:
			cur = start
			break
		case cur > at.Second[1]:
			cur = start
			//通知分钟下移
			at.Wg.Add(1)
			at.MinuteChan <- 1
			break
		default:
			cur = cur - (cur-at.Second[0])%every + every
		}
		at.SecondV = cur
	} else {
		secPassed := time.Now().Unix() - lastRun.Unix()
		at.SecondV = cur + every - int(secPassed)
	}

	at.Wg.Done()
	//启动一个监听
	for {
		switch <-at.SecondChan {
		case 0: //重置事件
			//设置为初始值
			at.SecondV = start
			at.Wg.Done()
			break
		case 1: //下移事件
			//末端不会触发下移事件
			//这里无需处理
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextMinute(now time.Time) {
	var calc = func(nowTime time.Time) {
		at.Wg.Add(1)
		next := nowTime.Minute()
		if at.Minute[0] != -1 {
			switch {
			case next < at.Minute[0]:
				next = at.Minute[0]
				//通知秒重置
				at.Wg.Add(1)
				at.SecondChan <- 0
				break
			case next > at.Minute[1]:
				next = at.Minute[0]
				//通知后序下移
				at.Wg.Add(1)
				at.HourChan <- 1
				//通知秒重置
				at.Wg.Add(1)
				at.SecondChan <- 0
				break
			}
		}
		at.MinuteV = next
		at.Wg.Done()
	}
	calc(now)
	at.Wg.Done()
	//启动一个监听
	for {
		switch <-at.MinuteChan {
		case 0: //重置
			//设置为初始值
			if at.Minute[0] != -1 {
				at.MinuteV = at.Minute[0]
			} else {
				at.MinuteV = 0
			}
			//重置完应该也通知前序重置
			at.SecondChan <- 0
			break
		case 1: //(前序)落在区间右侧，通知自己下移
			calc(now.Add(1 * time.Minute))
			at.Wg.Done()
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextHour(now time.Time) {
	var calc = func(nowTime time.Time) {
		at.Wg.Add(1)
		next := nowTime.Hour()
		if at.Hour[0] != -1 {
			switch {
			case next < at.Hour[0]:
				next = at.Hour[0]
				//通知前序重置
				at.MinuteChan <- 0
				break
			case next > at.Hour[1]:
				next = at.Hour[0]
				//通知后序下移
				at.Wg.Add(1)
				at.DayChan <- 1
				//通知前序重置
				at.Wg.Add(1)
				at.MinuteChan <- 0
				break
			}
		}
		at.HourV = next
		at.Wg.Done()
	}
	calc(now)
	at.Wg.Done()
	//启动一个监听
	for {
		switch <-at.HourChan {
		case 0: //重置
			//设置为初始值
			if at.Hour[0] != -1 {
				at.HourV = at.Hour[0]
			} else {
				at.HourV = 0
			}
			//重置完应该也通知前序重置
			at.MinuteChan <- 0
			break
		case 1: //(前序)落在区间右侧，通知自己下移
			calc(now.Add(1 * time.Hour))
			at.Wg.Done()
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextDay(now time.Time) {
	var calc = func(nowTime time.Time) {
		at.Wg.Add(1)
		next := nowTime.Day()
		if at.Day[0] != -1 {
			switch {
			case next < at.Day[0]:
				next = at.Day[0]
				//通知前序重置
				at.Wg.Add(1)
				at.HourChan <- 0
				break
			case next > at.Day[1]:
				next = at.Day[0]
				//通知前序重置
				at.Wg.Add(1)
				at.HourChan <- 0
				//通知后序下移
				at.Wg.Add(1)
				at.MonthChan <- 1
				break
			}
		}
		at.DayV = next
		at.Wg.Done()
	}
	calc(now)
	at.Wg.Done()
	//启动一个监听
	for {
		switch <-at.DayChan {
		case 0: //重置
			//设置为初始值
			if at.Day[0] != -1 {
				at.DayV = at.Day[0]
			} else {
				at.DayV = 1
			}
			//重置完应该也通知前序重置
			at.HourChan <- 0
			break
		case 1: //(前序)落在区间右侧，通知自己下移
			calc(now.AddDate(0, 0, 1))
			at.Wg.Done()
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextMonth(now time.Time) {
	var calc = func(nowTime time.Time) {
		at.Wg.Add(1)
		next := int(nowTime.Month())
		if at.Month[0] != -1 {
			switch {
			case next < at.Month[0]:
				next = at.Month[0]
				//通知前序重置
				at.Wg.Add(1)
				at.DayChan <- 0
				break
			case next > at.Month[1]:
				next = at.Month[0]
				//通知前序重置
				at.Wg.Add(1)
				at.DayChan <- 0
				//通知后序下移
				at.Wg.Add(1)
				at.YearChan <- 1
				break
			}
		}
		at.MonthV = next
		at.Wg.Done()
	}
	calc(now)
	at.Wg.Done()
	//启动一个监听
	for {
		switch <-at.MonthChan {
		case 0: //重置
			//设置为初始值
			if at.Month[0] != -1 {
				at.MonthV = at.Month[0]
			} else {
				at.MonthV = 1
			}
			//重置完应该也通知前序重置
			at.DayChan <- 0
			break
		case 1: //(前序)落在区间右侧，通知自己下移
			calc(now.AddDate(0, 1, 0))
			at.Wg.Done()
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextYear(now time.Time) {
	next := now.Year()
	at.YearV = next
	at.Wg.Done()
	for {
		switch <-at.YearChan {
		case 0: //重置
			//设置为初始值//不存在的逻辑
			break
		case 1: //(前序)落在区间右侧，通知自己下移
			at.YearV++
			at.Wg.Done()
			break
		case 9: //计算结束
			return
		}
	}
}
func (at *TimerAt) GetNextWeek(now time.Time) {
	var calc = func(nowTime time.Time) {
		at.Wg.Add(1)
		next := int(nowTime.Weekday()) //从0，周日 开始
		if next == 0 {
			next = 7
		}
		nextDay := nowTime.Day()
		if at.Week[0] != -1 {
			switch {
			case next < at.Week[0]:
				offset := at.Week[0] - next
				nextDay += offset
				//通知前序重置
				at.Wg.Add(1)
				at.HourChan <- 0
				break
			case next > at.Week[1]:
				//通知前序重置
				at.Wg.Add(1)
				at.HourChan <- 0
				//根据情况通知后序（月）下移
				//计算下移天数 eg: 配置2-4,当前 5，则下移=7-5+2=4
				offset := 7 - next + at.Week[0]
				//这里不需要通知月下移，修改日的数据实现月的下移
				//比如4月29，下移5天，日变成了34，在计算日期的时候由于日超出了30的上限，月会自动下移
				nextDay += offset
				break
			}
		}
		at.DayV = nextDay
		at.MonthV = int(nowTime.Month())
		at.YearV = nowTime.Year()
		at.Wg.Done()
	}
	calc(now)
	at.Wg.Done()
}

func init() {
	manager = &Manager{
		Tasks:     make(map[string]*Task),
		NextTime:  time.Now().AddDate(100, 0, 0),
		NextTasks: make([]*Task, 0),
	}
}
